package personaje;

import java.awt.event.KeyEvent;
import java.util.Date;
import java.util.Stack;

import logica.*;
import nivel.*;
import oyente.*;
import powerup.PowerUp;
import sprite.personajes.SpriteMario;
import sprite.personajes.SpritePersonaje;

/**
 * Clase que modela al personaje principal del juego
 * 
 * @author Agustin Orge (LU.85538)
 * @author Victoria Martinez de la Cruz (LU.87620)
 */
public class Mario extends Personaje {

	/** Oyente asociado al personaje */
	protected Manejo m;
	/** Coleccion de powerups */
	protected Stack<PowerUp> powerups;
	/** Logica de mario */
	protected LogicaMario control;
	/** Si tiene estrella */
	private boolean estrellado;
	/** Tiempo en milisegundos de la ultima estrella tomada */
	private long inicioEstrella;
	/** Graficas de mario */
	protected SpriteMario skins;
	/** Si posee invulnerabilidad temporal */
	protected boolean invulnerabilidadTemporal;
	/** Tiempo en milisegundos del inicio de la invulerabilidad */
	protected long inicioInvulerabilidadTemporal;

	/**
	 * Constructor de Mario
	 * 
	 * Encargado de inicializar la logica, el casillero, el oyente, y el icono
	 * 
	 * @param n
	 *            nivel en el que se encuentra mario
	 */
	public Mario(Nivel n) {
		super();
		control = new LogicaMario(this);
		setCasillero(new Casillero(336, 192));
		setNivel(n);
		m = new Manejo(this);
		skins = new SpriteMario();
		icono = skins.getSpriteMario();
		powerups = new Stack<PowerUp>();
		estrellado = false;
		inicioEstrella = 0;
		invulnerabilidadTemporal = false;
	}

	// Comandos
	/*
	 * (non-Javadoc)
	 * 
	 * @see personaje.Personaje#moverDerecha()
	 */
	public void moverDerecha() {
		if (control.moverDerecha(this)) {
			casilleroActual.getCoordenada().setX(casilleroActual.getX() + 8);
			nivel.getGrafica().getManejador().moverDerecha();

		}
		if (casilleroActual.getX() == 3264) {
			nivel.frenarHilos();
			nivel.ganoNivel();
		}
	}

	@Override
	public void moverIzquierda() {

		if (control.moverIzquierda(this)) {
			casilleroActual.getCoordenada().setX(casilleroActual.getX() - 8);
			nivel.getGrafica().getManejador().moverIzquierda();
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see personaje.Personaje#saltar()
	 */
	public void saltar() {

		if (control.saltar(this) && ((LogicaMario) control).cantSaltos()) {
			((LogicaMario) control).setSaltando(true);
			this.setCayendo(false);
			casilleroActual.getCoordenada().setY(casilleroActual.getY() - 16);
			((LogicaMario) control).aumentarSaltos();
			nivel.getGrafica().getManejador().moverArriba();
		} else if (!control.saltar(this) && (powerups.size() > 0)) {
			romperBloques();
		} else if (!this.estaCayendo()) {
			((LogicaMario) control).setSaltando(false);

		}

	}

	@Override
	public void caer() {

		if (control.caer(this)) {
			this.setCayendo(true);
			((LogicaMario) control).setSaltando(false);
			casilleroActual.getCoordenada().setY(casilleroActual.getY() + 16);
			nivel.getGrafica().getManejador().moverAbajo();

			Enemigo e = hayEnemigoAbajo(casilleroActual.getCoordenada());
			// Controla si hay enemigo en el casillero en el que cayo, si hay
			// el enemigo muere
			if (e != null) {

				e.morir();

				this.rebotar();

			}

		} else if (!((LogicaMario) control).estaSaltando()) {
			this.setCayendo(false);
			((LogicaMario) control).resetearSaltos();
		}

	}

	/**
	 * Metodo utilizado para realizar un rebote luego de matar un enemigo
	 */
	private void rebotar() {
		((LogicaMario) control).resetearSaltos();
		this.setCayendo(false);
		((LogicaMario) control).setSaltando(true);

		for (int i = 0; i < 4; i++) {
			saltar();
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		this.setCayendo(true);
		((LogicaMario) control).setSaltando(false);

	}

	/**
	 * Metodo utilizado para realizar un salto mientras se mueve hacia la
	 * izquierda
	 */
	public void saltarIzquierda() {
		saltar();
		moverIzquierda();
	}

	/**
	 * Metodo utilizado para realizar un salto mientras se mueve hacia la
	 * derecha
	 */
	public void saltarDerecha() {
		saltar();
		moverDerecha();
	}

	/**
	 * Retorna el oyente asociado a Mario
	 * 
	 * @return manejo
	 */
	public Manejo getManejo() {
		return m;
	}

	/**
	 * Agarrar un power up
	 * 
	 * @param pu
	 *            - power up
	 */
	public void agarrarPowerUp(PowerUp pu) {
		pu.agarrar();
	}

	@Override
	public Logica getControl() {
		return control;
	}

	/**
	 * Reporta si hay un enemigo debajo de mario
	 * 
	 * @param c
	 *            - coordenada
	 * @return enemigo, si hay - null, en caso contrario
	 */
	private Enemigo hayEnemigoAbajo(Coordenada c) {
		return nivel.getDirectorEnemigos().buscarEnemigoAbajo(c);
	}

	/**
	 * Metodo utilizado para agarrar una estrella
	 * 
	 * @param inicio
	 *            - tiempo en milisegundos del inicio
	 */
	public void agarroEstrella(long inicio) {
		inicioEstrella = inicio;
		if (estrellado) {
			nivel.getScore().modificarPuntaje(20);
		} else {
			nivel.getScore().modificarPuntaje(35);
		}
		estrellado = true;
	}

	/**
	 * Controla el tiempo de duracion de la estrella
	 */
	public void controlEstrella() {
		Date d = new Date();
		long actual = d.getTime();

		if (estrellado && (actual - inicioEstrella) >= 15000) {

			estrellado = false;

			if (powerups.size() == 0) {
				icono = skins.getSpriteMario();
			} else if (powerups.size() == 1) {
				icono = skins.getSpriteSuperMario();
				nivel.getGrafica().getPanelMario().setPanelSuperMario();
			} else if (powerups.size() == 2) {
				icono = skins.getSpriteMarioFiery();
				nivel.getGrafica().getPanelMario().setPanelSuperMario();
			}

		}

	}

	@Override
	public void morir() {

		if (casilleroActual.getY() > 208) {
			nivel.getScore().modificarPuntaje(15);
		}
		m.desactivarTeclado();
		nivel.getGrafica().getPanelMario().morir();
		nivel.frenarHilos();

		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		nivel.perdioNivel();

	}

	/**
	 * Metodo utilizado para comunicarle a un enemigo si mato a mario
	 * 
	 * @param en
	 *            - enemigo
	 */
	public void morir(Enemigo en) {

		if (!invulnerabilidadTemporal) {
			if ((powerups.empty())) {

				en.mato();
				morir();
			} else {
				recibioAtaque();
			}
		}

	}

	/**
	 * Metodo utilizado para representar cuando mario recive un ataque
	 */
	private void recibioAtaque() {
		activarInvulnerabilidadTemporal();
		if (powerups.size() == 1) {
			setGraficaMario();
			control = new LogicaMario(this);
		} else if (powerups.size() == 2) {
			setGraficaSuperMario();
		}

		powerups.pop();

	}

	/**
	 * Controla si muere mario o un enemigo
	 * 
	 * @param e
	 *            - enemigo
	 */
	public void controlQuienMuere(Enemigo e) {
		if (estrellado) {
			e.morir();
		} else {
			morir(e);
		}
	}

	/**
	 * Setea los iconos de mario
	 * 
	 * @param nuevosIconos
	 */
	public void setIcono(SpritePersonaje nuevosIconos) {
		icono = nuevosIconos;
	}

	/**
	 * Retorna los iconos de mario
	 * 
	 * @return iconos
	 */
	public SpriteMario getSkins() {
		return skins;
	}

	/**
	 * Setea la logica para mario grande
	 */
	public void setControlGrande() {
		control = new LogicaMarioGrande(this);
	}

	/**
	 * Retorna la cantidad de power ups que tiene mario
	 * 
	 * @return cantidad
	 */
	public int cantPowerUps() {
		return powerups.size();
	}

	/**
	 * Agrega un power up a la coleccion
	 * 
	 * @param pu
	 *            - power up
	 */
	public void agregarPowerUp(PowerUp pu) {
		powerups.push(pu);
	}

	/**
	 * Setea la grafica para mario pequeño
	 */
	private void setGraficaMario() {
		nivel.getGrafica().getPanelMario().setPanelMario();
		icono = skins.getSpriteMario();

		int rumbo = nivel.getMario().getManejo().getLastKey();

		if (rumbo == KeyEvent.VK_LEFT) {
			nivel.getGrafica().getPanelMario().izquierda();
		} else {
			nivel.getGrafica().getPanelMario().derecha();
		}
	}

	/**
	 * Setea la grafica para mario grande
	 */
	private void setGraficaSuperMario() {
		nivel.getGrafica().getPanelMario().setPanelSuperMario();
		icono = skins.getSpriteSuperMario();

		int rumbo = nivel.getMario().getManejo().getLastKey();

		if (rumbo == KeyEvent.VK_LEFT) {
			nivel.getGrafica().getPanelMario().izquierda();
		} else {
			nivel.getGrafica().getPanelMario().derecha();
		}
	}

	/**
	 * Disparar bolas de fuego
	 */
	public void disparar() {
		if (!powerups.empty()) {
			powerups.peek().accion();
		}
	}

	/**
	 * Controla si mario puede romper un bloque o no
	 */
	private void romperBloques() {

		Casillero bloqueAromper = nivel.getTablero().buscarCasillero(
				casilleroActual.getX(), casilleroActual.getY() - 32);

		Casillero bloqueAromperIzq = nivel.getTablero().buscarCasillero(
				casilleroActual.getX() - 8, casilleroActual.getY() - 32);

		Casillero bloqueAromperDer = nivel.getTablero().buscarCasillero(
				casilleroActual.getX() + 8, casilleroActual.getY() - 32);

		if (bloqueAromper != null) {
			if (!bloqueAromper.hayPowerUp()) {
				bloqueAromper.getObstaculo().romper(nivel);
				((LogicaMario) control).setSaltando(false);
			}
		} else if (bloqueAromperIzq != null) {
			if (!bloqueAromperIzq.hayPowerUp()) {
				bloqueAromperIzq.getObstaculo().romper(nivel);
				((LogicaMario) control).setSaltando(false);
			}
		} else if (bloqueAromperDer != null) {
			if (!bloqueAromperDer.hayPowerUp()) {
				bloqueAromperDer.getObstaculo().romper(nivel);
				((LogicaMario) control).setSaltando(false);
			}
		}
	}

	/**
	 * Resetea la invulnerabilidad temporal
	 */
	public void resetInvulnerabilidadTemporal() {
		Date d = new Date();
		if (invulnerabilidadTemporal
				&& (d.getTime() - inicioInvulerabilidadTemporal >= 500)) {
			invulnerabilidadTemporal = false;
		}
	}

	/**
	 * Activa la invulnerabilidad temporal
	 */
	private void activarInvulnerabilidadTemporal() {
		Date d = new Date();
		inicioInvulerabilidadTemporal = d.getTime();
		invulnerabilidadTemporal = true;
	}

	/**
	 * Reporta si mario tiene estrella o no
	 * 
	 * @return true, si tiene - false, en caso contrario
	 */
	public boolean tieneEstrella() {
		return estrellado;
	}
}
